package com.kedacom.ctsp.authz.security;

import com.kedacom.ctsp.authz.security.checker.DefaultPostAuthenticationChecks;
import com.kedacom.ctsp.authz.security.checker.DefaultPreAuthenticationChecks;
import com.kedacom.ctsp.authz.security.checker.PostAuthenticationChecker;
import com.kedacom.ctsp.authz.security.checker.PreAuthenticationChecker;
import com.kedacom.ctsp.authz.security.provider.JsonAuthenticationFilter;
import com.kedacom.ctsp.authz.security.provider.RestLogoutSuccessHandler;
import com.kedacom.ctsp.authz.security.provider.TokenLoginFilter;
import com.kedacom.ctsp.authz.security.provider.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.web.cors.CorsUtils;

/**
 * 配置Spring Security初始化
 *
 * @author xuwei
 * @create 2017-11-30 19:34
 **/
@EnableConfigurationProperties(AuthzSecurityProperties.class)
public class AuthzWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthzSecurityProperties securityProperties;

    @Autowired
    private SessionRegistry sessionRegistry;

    @Autowired
    private AuthenticationFailureHandler loginFailureHandler;
    @Autowired
    private AuthenticationSuccessHandler loginSuccessHandler;
    @Autowired
    private RestLogoutSuccessHandler logoutSuccessHandler;
    @Autowired
    private TokenLoginFilter tokenLoginFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //用重写的Filter替换掉原有的UsernamePasswordAuthenticationFilter
        http
                .addFilterAt(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(tokenLoginFilter, UsernamePasswordAuthenticationFilter.class);
        http
                .authorizeRequests()
                //任何人都可以访问
                .antMatchers(securityProperties.getPermitAllUrl()).permitAll()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .anyRequest().authenticated()
                .antMatchers(securityProperties.getLoginUrl(), securityProperties.getLogoutSuccessUrl(), securityProperties.getAccessDeniedUrl()).permitAll()
                .and().formLogin()
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)
                .loginPage(securityProperties.getLoginUrl()).usernameParameter(securityProperties.getLoginUsernameParam()).passwordParameter(securityProperties.getLoginPasswordParam())
                .loginProcessingUrl(securityProperties.getLoginProcessingUrl())
//                .defaultSuccessUrl(securityProperties.getDefaultSuccessUrl())
                .and().exceptionHandling().accessDeniedPage(securityProperties.getAccessDeniedUrl())
                .and().logout().logoutUrl(securityProperties.getLogoutUrl())
                .logoutSuccessHandler(logoutSuccessHandler)
//                .logoutSuccessUrl(securityProperties.getLogoutSuccessUrl())
                .and()
                .rememberMe()
                .key("rme")
                .rememberMeParameter(securityProperties.getRememberMeParameter())
                .rememberMeCookieName(securityProperties.getRememberMeCookieName())
                .tokenValiditySeconds(securityProperties.getTokenValiditySeconds())
                .and()
                .httpBasic()
                .and()
                .csrf().disable()
                .cors()
                .and()
                .sessionManagement()
                .maximumSessions(securityProperties.getMaximumSessions())
                .maxSessionsPreventsLogin(false)
                .expiredUrl(securityProperties.getExpiredUrl())
                .sessionRegistry(sessionRegistry);

    }

    @Bean
    protected JsonAuthenticationFilter jsonAuthenticationFilter() throws Exception {
        JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(loginSuccessHandler);
        filter.setAuthenticationFailureHandler(loginFailureHandler);
        filter.setFilterProcessesUrl(securityProperties.getLoginProcessingUrl());

        // 使用自定义的sessionRegistry，不然还会使用默认的sessionRegistry
        filter.setSessionAuthenticationStrategy(new RegisterSessionAuthenticationStrategy(sessionRegistry));

        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());

        return filter;
    }

    @Bean
    @ConditionalOnMissingBean
    protected PostAuthenticationChecker postAuthenticationChecker() {
        return new DefaultPostAuthenticationChecks();
    }

    @Bean
    @ConditionalOnMissingBean
    protected PreAuthenticationChecker preAuthenticationChecker() {
        return new DefaultPreAuthenticationChecks();
    }

    @Bean
    public org.springframework.security.crypto.password.PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @ConditionalOnMissingBean
    @Override
    protected UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setPreAuthenticationChecks(preAuthenticationChecker());
        authProvider.setPostAuthenticationChecks(postAuthenticationChecker());
        authProvider.setPasswordEncoder(passwordEncoder());
        authProvider.setUserDetailsService(userDetailsService());
        auth.authenticationProvider(authProvider);
    }


}
